<template>
  <div class="bpmn-designer">
    <!-- 工具栏 -->
    <div class="designer-toolbar">
      <Space>
        <Button type="primary" @click="handleSave">
          <template #icon><SaveOutlined /></template>
          保存
        </Button>
        <Button @click="handleUndo">
          <template #icon><UndoOutlined /></template>
          撤销
        </Button>
        <Button @click="handleRedo">
          <template #icon><RedoOutlined /></template>
          重做
        </Button>
        <Button @click="handleZoomIn">
          <template #icon><PlusOutlined /></template>
          放大
        </Button>
        <Button @click="handleZoomOut">
          <template #icon><MinusOutlined /></template>
          缩小
        </Button>
        <Button @click="handleZoomReset">
          <template #icon><FullscreenOutlined /></template>
          适应画布
        </Button>
        <Button @click="handleDownload">
          <template #icon><DownloadOutlined /></template>
          下载
        </Button>
      </Space>
    </div>

    <!-- 设计器容器 -->
    <div class="designer-container">
      <!-- BPMN 画布 -->
      <div ref="canvasRef" class="designer-canvas"></div>

      <!-- 属性面板 -->
      <div v-if="showProperties" class="designer-properties">
        <Card title="属性配置" size="small">
          <div v-if="selectedElement">
            <Form layout="vertical" :model="elementProperties">
              <FormItem label="ID">
                <Input v-model:value="elementProperties.id" disabled />
              </FormItem>
              <FormItem label="名称">
                <Input
                  v-model:value="elementProperties.name"
                  @change="updateElementName"
                />
              </FormItem>
              <FormItem v-if="isUserTask" label="办理人">
                <Input
                  v-model:value="elementProperties.assignee"
                  placeholder="例如：${assignee}"
                  @change="updateElementProperty('assignee')"
                />
              </FormItem>
              <FormItem v-if="isUserTask" label="候选人">
                <Input
                  v-model:value="elementProperties.candidateUsers"
                  placeholder="多个用逗号分隔"
                  @change="updateElementProperty('candidateUsers')"
                />
              </FormItem>
              <FormItem v-if="isUserTask" label="候选组">
                <Input
                  v-model:value="elementProperties.candidateGroups"
                  placeholder="多个用逗号分隔"
                  @change="updateElementProperty('candidateGroups')"
                />
              </FormItem>
              <FormItem v-if="isSequenceFlow" label="条件表达式">
                <TextArea
                  v-model:value="elementProperties.conditionExpression"
                  :rows="3"
                  placeholder="例如：${approved == true}"
                  @change="updateElementProperty('conditionExpression')"
                />
              </FormItem>
            </Form>
          </div>
          <Empty v-else description="请选择一个元素" />
        </Card>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, ref, computed } from 'vue';

import {
  Button,
  Card,
  Empty,
  Form,
  FormItem,
  Input,
  Space,
  message,
} from 'ant-design-vue';

const { TextArea } = Input;
import {
  DownloadOutlined,
  FullscreenOutlined,
  MinusOutlined,
  PlusOutlined,
  RedoOutlined,
  SaveOutlined,
  UndoOutlined,
} from '@ant-design/icons-vue';
import BpmnModeler from 'bpmn-js/lib/Modeler';
// 引入中文翻译
import customTranslate from './customTranslate';

interface Props {
  /** BPMN XML */
  bpmnXml?: string;
  /** 是否显示属性面板 */
  showProperties?: boolean;
}

interface Emits {
  (e: 'save', xml: string): void;
  (e: 'update:bpmnXml', xml: string): void;
}

const props = withDefaults(defineProps<Props>(), {
  bpmnXml: '',
  showProperties: true,
});

const emit = defineEmits<Emits>();

const canvasRef = ref<HTMLDivElement>();
let modeler: BpmnModeler | null = null;

const selectedElement = ref<any>(null);
const elementProperties = ref<any>({});

// 计算属性：判断元素类型
const isUserTask = computed(() => {
  return selectedElement.value?.type === 'bpmn:UserTask';
});

const isSequenceFlow = computed(() => {
  return selectedElement.value?.type === 'bpmn:SequenceFlow';
});

// 默认的 BPMN XML
const defaultBpmnXml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
                  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
                  xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
                  xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
                  id="Definitions_1"
                  targetNamespace="http://bpmn.io/schema/bpmn">
  <bpmn:process id="Process_1" isExecutable="true">
    <bpmn:startEvent id="StartEvent_1" name="开始"/>
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="159" width="36" height="36"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds x="186" y="202" width="22" height="14"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>`;

// 初始化 BPMN 设计器
async function initModeler() {
  if (!canvasRef.value) return;

  modeler = new BpmnModeler({
    container: canvasRef.value,
    keyboard: {
      bindTo: document,
    },
    // 添加中文翻译模块
    additionalModules: [
      {
        translate: ['value', customTranslate],
      },
    ],
  });

  // 导入 BPMN XML
  try {
    await modeler.importXML(props.bpmnXml || defaultBpmnXml);

    // 适应画布
    const canvas = modeler.get('canvas');
    canvas.zoom('fit-viewport');

    // 监听元素选择事件
    const eventBus = modeler.get('eventBus');
    eventBus.on('selection.changed', handleSelectionChanged);
    eventBus.on('element.changed', handleElementChanged);
  } catch (error: any) {
    message.error(`导入流程图失败: ${error.message}`);
  }
}

// 处理元素选择变化
function handleSelectionChanged(event: any) {
  const { newSelection } = event;
  if (newSelection && newSelection.length > 0) {
    selectedElement.value = newSelection[0];
    loadElementProperties();
  } else {
    selectedElement.value = null;
    elementProperties.value = {};
  }
}

// 处理元素变化
function handleElementChanged() {
  if (selectedElement.value) {
    loadElementProperties();
  }
}

// 加载元素属性
function loadElementProperties() {
  if (!selectedElement.value) return;

  const element = selectedElement.value;
  const businessObject = element.businessObject;

  elementProperties.value = {
    id: businessObject.id || '',
    name: businessObject.name || '',
    assignee: businessObject.assignee || '',
    candidateUsers: businessObject.candidateUsers || '',
    candidateGroups: businessObject.candidateGroups || '',
    conditionExpression: businessObject.conditionExpression?.body || '',
  };
}

// 更新元素名称
function updateElementName() {
  if (!modeler || !selectedElement.value) return;

  const modeling = modeler.get('modeling');
  modeling.updateProperties(selectedElement.value, {
    name: elementProperties.value.name,
  });
}

// 更新元素属性
function updateElementProperty(propertyName: string) {
  if (!modeler || !selectedElement.value) return;

  const modeling = modeler.get('modeling');
  const properties: any = {};

  if (propertyName === 'conditionExpression') {
    const moddle = modeler.get('moddle');
    const conditionExpression = moddle.create('bpmn:FormalExpression', {
      body: elementProperties.value.conditionExpression,
    });
    properties.conditionExpression = conditionExpression;
  } else {
    properties[propertyName] = elementProperties.value[propertyName];
  }

  modeling.updateProperties(selectedElement.value, properties);
}

// 保存
async function handleSave() {
  if (!modeler) return;

  try {
    const { xml } = await modeler.saveXML({ format: true });
    emit('save', xml || '');
    emit('update:bpmnXml', xml || '');
    message.success('保存成功');
  } catch (error: any) {
    message.error(`保存失败: ${error.message}`);
  }
}

// 撤销
function handleUndo() {
  if (!modeler) return;
  const commandStack = modeler.get('commandStack');
  commandStack.undo();
}

// 重做
function handleRedo() {
  if (!modeler) return;
  const commandStack = modeler.get('commandStack');
  commandStack.redo();
}

// 放大
function handleZoomIn() {
  if (!modeler) return;
  const canvas = modeler.get('canvas');
  const currentZoom = canvas.zoom();
  canvas.zoom(currentZoom + 0.1);
}

// 缩小
function handleZoomOut() {
  if (!modeler) return;
  const canvas = modeler.get('canvas');
  const currentZoom = canvas.zoom();
  canvas.zoom(currentZoom - 0.1);
}

// 适应画布
function handleZoomReset() {
  if (!modeler) return;
  const canvas = modeler.get('canvas');
  canvas.zoom('fit-viewport');
}

// 下载
async function handleDownload() {
  if (!modeler) return;

  try {
    const { xml } = await modeler.saveXML({ format: true });
    const blob = new Blob([xml || ''], { type: 'application/xml' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'process.bpmn';
    link.click();
    URL.revokeObjectURL(url);
    message.success('下载成功');
  } catch (error: any) {
    message.error(`下载失败: ${error.message}`);
  }
}

onMounted(() => {
  initModeler();
});

onUnmounted(() => {
  if (modeler) {
    modeler.destroy();
  }
});
</script>

<style scoped>
.bpmn-designer {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #f5f5f5;
}

.designer-toolbar {
  padding: 12px 16px;
  background: #fff;
  border-bottom: 1px solid #e8e8e8;
}

.designer-container {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.designer-canvas {
  flex: 1;
  background: #fff;
}

.designer-properties {
  width: 300px;
  background: #fff;
  border-left: 1px solid #e8e8e8;
  overflow-y: auto;
}
</style>

<style>
/* BPMN.js 样式 */
@import 'bpmn-js/dist/assets/diagram-js.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
</style>

